#pragma once
#include "Noncopyable.h"
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/spdlog.h"
#include <chrono>
#include <ctime>
#include <iostream>
#include <memory>
#include <string>

class Logger : public Noncopyable {
  public:
    static Logger *getInstance();
    static std::string formatToString(const char *format, ...);
    std::shared_ptr<spdlog::logger> getLogger();

  private:
    std::string currentTimeAsString();

    Logger();

    ~Logger();

    void *operator new(size_t size) = delete;
    Logger(const Logger &) = delete;
    Logger &operator=(const Logger &) = delete;

  private:
    std::shared_ptr<spdlog::logger> logger_;
};

#ifdef LOG_OFF
#define LOG_TRACE(...)                                                         \
    do {                                                                       \
    } while (0);
#define LOG_DEBUG(...)                                                         \
    do {                                                                       \
    } while (0)
;

#define LOG_INFO(...)                                                          \
    do {                                                                       \
    } while (0);

#define LOG_ERROR(...)                                                         \
    do {                                                                       \
    } while (0);

#define LOG_FATAL(...)                                                         \
    do {                                                                       \
        exit(1);                                                               \
    } while (0);
#else
#define LOG_TRACE(...)                                                         \
    do {                                                                       \
        std::string msg = Logger::formatToString(__VA_ARGS__);                 \
        SPDLOG_LOGGER_CALL(Logger::getInstance()->getLogger().get(),           \
                           spdlog::level::trace, msg);                         \
    } while (0);
#define LOG_DEBUG(...)                                                         \
    do {                                                                       \
        std::string msg = Logger::formatToString(__VA_ARGS__);                 \
        SPDLOG_LOGGER_CALL(Logger::getInstance()->getLogger().get(),           \
                           spdlog::level::debug, msg);                         \
    } while (0);

#define LOG_INFO(...)                                                          \
    do {                                                                       \
        std::string msg = Logger::formatToString(__VA_ARGS__);                 \
        SPDLOG_LOGGER_CALL(Logger::getInstance()->getLogger().get(),           \
                           spdlog::level::info, msg);                          \
    } while (0);

#define LOG_ERROR(...)                                                         \
    do {                                                                       \
        std::string msg = Logger::formatToString(__VA_ARGS__);                 \
        SPDLOG_LOGGER_CALL(Logger::getInstance()->getLogger().get(),           \
                           spdlog::level::err, msg);                           \
    } while (0);

#define LOG_FATAL(...)                                                         \
    do {                                                                       \
        std::string msg = Logger::formatToString(__VA_ARGS__);                 \
        SPDLOG_LOGGER_CALL(Logger::getInstance()->getLogger().get(),           \
                           spdlog::level::critical, msg);                      \
        exit(1);                                                               \
    } while (0);
#endif
